﻿// -----------------------------------------------------------------------
// <copyright file="StatusCode.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Common
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;

	/// <summary>
	/// Defines PAP and RIM specific status codes.
	/// </summary>
	public class StatusCode
	{
		private static readonly Dictionary<string, StatusCode> _globalCodes;

		/// <summary>
		/// The request was completed successfully.
		/// </summary>
		public static readonly StatusCode OK = new StatusCode("1000", "The request was completed successfully.");

		/// <summary>
		/// The request was accepted for processing.
		/// </summary>
		public static readonly StatusCode Accepted = new StatusCode("1001", "The request was accepted for processing.");

		/// <summary>
		/// The request has been accepted for processing, but the final outcome is not yet known.
		/// </summary>
		public static readonly StatusCode OverSoftPushQuota = new StatusCode("1500", "The request has been accepted for processing, but the final outcome is not yet known. The PPG is indicating that the daily push count quota has been exceeded and you are approaching the hard limit where pushes will start being rejected. Future pushes should be delayed until the next day when more quota is available.");

		/// <summary>
		/// The request is invalid.
		/// </summary>
		public static readonly StatusCode BadRequest = new StatusCode("2000", "The request is invalid.");

		/// <summary>
		/// The requested action is forbidden.
		/// </summary>
		public static readonly StatusCode Forbidden = new StatusCode("2001", "The requested action is forbidden.");

		/// <summary>
		/// The specified PIN is not recognised.
		/// </summary>
		public static readonly StatusCode AddressError = new StatusCode("2002", "The specified PIN is not recognised.");

		/// <summary>
		/// The specified web address is invalid.
		/// </summary>
		public static readonly StatusCode AddressNotFound = new StatusCode("2003", "The specified web address is invalid.");

		/// <summary>
		/// The BlackBerry Push Service could not find the specified Push ID.
		/// </summary>
		public static readonly StatusCode PushIdNotFound = new StatusCode("2004", "The BlackBerry Push Service could not find the specified Push ID.");

		/// <summary>
		/// The request specifies a function that is not available in the Push Initiator.
		/// </summary>
		public static readonly StatusCode InvalidInitiatorFunction = new StatusCode("2005", "The request specifies a function that is not available in the Push Initiator.");

		/// <summary>
		/// The request specifies a function that the Push Initiator does not support.
		/// </summary>
		public static readonly StatusCode NotSupportedInitiatorFunction = new StatusCode("2006", "The request specifies a function that the Push Initiator does not support.");

		/// <summary>
		/// The supplied Push ID is not unique.
		/// </summary>
		public static readonly StatusCode DuplicatePushId = new StatusCode("2007", "The supplied Push ID is not unique.");

		/// <summary>
		/// The Push ID is valid but the push request could not be cancelled.
		/// </summary>
		public static readonly StatusCode CancellationNotPossible = new StatusCode("2008", "The Push ID is valid but the push request could not be cancelled.");

		/// <summary>
		/// The Push ID specified was found, but the corresponding address(es) are still being processed.
		/// </summary>
		public static readonly StatusCode StatusQueryNotPossible = new StatusCode("2009", "The Push ID specified was found, but the corresponding address(es) are still being processed. Status query is not possible at this time, try again later.");

		/// <summary>
		/// The request has been rejected by the PPG because the daily push count quota has been exceeded.
		/// </summary>
		public static readonly StatusCode OverHardPushQuota = new StatusCode("2500", "The request has been rejected by the PPG because the daily push count quota has been exceeded. Future pushes should be delayed until the next day when more quota is available.");

		/// <summary>
		/// The BlackBerry infrastructure could not complete the request due to an internal error.
		/// </summary>
		public static readonly StatusCode InternalServerError = new StatusCode("3000", "The BlackBerry infrastructure could not complete the request due to an internal error.");

		/// <summary>
		/// The server does not support the operation that was requested.
		/// </summary>
		public static readonly StatusCode NotImplemented = new StatusCode("3001", "The server does not support the operation that was requested.");

		/// <summary>
		/// The server does not support the PAP version specified in the request.
		/// </summary>
		public static readonly StatusCode VersionNotSupported = new StatusCode("3002", "The server does not support the PAP version specified in the request.");

		/// <summary>
		/// The action cannot be completed because the message is no longer available.
		/// </summary>
		public static readonly StatusCode SpecifiedDeliveryMethodNotSupported = new StatusCode("3003", "The action cannot be completed because the message is no longer available.");

		/// <summary>
		/// The PPG does not support some Push Initiator capability information provided in the push message.
		/// </summary>
		public static readonly StatusCode CapabilitiesNotSupported = new StatusCode("3004", "The PPG does not support some Push Initiator capability information provided in the push message.");

		/// <summary>
		/// The PPG does not support the requested operation for multiple recipients.
		/// </summary>
		public static readonly StatusCode OperationNotSupportedForMultipleRecipients = new StatusCode("3005", "The PPG does not support the requested operation for multiple recipients.");

		/// <summary>
		/// The PPG could not transform the message.
		/// </summary>
		public static readonly StatusCode UnableToTransformMessage = new StatusCode("3006", "The PPG could not transform the message.");

		/// <summary>
		/// The PPG could not deliver the message using the specified method.
		/// </summary>
		public static readonly StatusCode UnableToDeliverMessageUsingDeliveryMethod = new StatusCode("3007", "The PPG could not deliver the message using the specified method.");

		/// <summary>
		/// The network that was requested is not available.
		/// </summary>
		public static readonly StatusCode NetworkUnavailable = new StatusCode("3009", "The network that was requested is not available.");

		/// <summary>
		/// The bearer that was requested is not available.
		/// </summary>
		public static readonly StatusCode BearerUnavailable = new StatusCode("3010", "The bearer that was requested is not available.");

		/// <summary>
		/// The PPG does not support the replace operation.
		/// </summary>
		public static readonly StatusCode ReplaceNotSupported = new StatusCode("3011", "The PPG does not support the replace operation.");

		/// <summary>
		/// The service failed.
		/// </summary>
		public static readonly StatusCode ServiceFailure = new StatusCode("4000", "The service failed.");

		/// <summary>
		/// The server is busy.
		/// </summary>
		public static readonly StatusCode ServiceUnavailable = new StatusCode("4001", "The server is busy.");

		/// <summary>
		/// The request expired.
		/// </summary>
		public static readonly StatusCode Expired = new StatusCode("4500", "The request expired.");

		/// <summary>
		/// The request failed.
		/// </summary>
		public static readonly StatusCode Failed = new StatusCode("4501", "The request failed.");

		/// <summary>
		/// The request failed because the BlackBerry device port number is closed.
		/// </summary>
		public static readonly StatusCode PortClosed = new StatusCode("4502", "The request failed because the BlackBerry device port number is closed.");

		/// <summary>
		/// Device unable to receive push due to push service being blocked.
		/// </summary>
		public static readonly StatusCode ServiceBlocked = new StatusCode("4503", "Device unable to receive push due to push service being blocked. This may happen, for example, if the user has cancelled or changed their carrier service plan, deactivated their BlackBerry smartphone, has run out of time on a pre-paid data plan, etc...");

		/// <summary>
		/// Push connection aborted by device.
		/// </summary>
		public static readonly StatusCode PushAbortedByDevice = new StatusCode("5234", "Push connection aborted by device.");

		/// <summary>
		/// The RIM specified request completed successfully.
		/// </summary>
		public static readonly StatusCode RimRequestOk = new StatusCode("21000", "The RIM specified request completed successfully.");

		/// <summary>
		/// The RIM specific request is badly formed.
		/// </summary>
		public static readonly StatusCode RimBadRequest = new StatusCode("22000", "The RIM specific request is badly formed.");

		/// <summary>
		/// The BlackBerry Push Service could not find the specified Push ID.
		/// </summary>
		public static readonly StatusCode RimPushIdNotFound = new StatusCode("22001", "The BlackBerry Push Service could not find the specified Push ID.");

		/// <summary>
		/// The specified web address in the RIM specific request is invalid.
		/// </summary>
		public static readonly StatusCode RimAddressError = new StatusCode("22002", "The specified web address in the RIM specific request is invalid.");

		/// <summary>
		/// The RIM specific request produces an incorrect status.
		/// </summary>
		public static readonly StatusCode RimIncorrectStatus = new StatusCode("22003", "The RIM specific request produces an incorrect status.");

		/// <summary>
		/// The RIM specific request produces no results.
		/// </summary>
		public static readonly StatusCode RimNoResults = new StatusCode("22004", "The RIM specific request produces no results.");

		/// <summary>
		/// The RIM specific request exceeds the number of calls allowed within the specified time period.
		/// </summary>
		public static readonly StatusCode RimExceedCallsInPeriod = new StatusCode("22005", "The RIM specific request exceeds the number of calls allowed within the specified time period.");

		/// <summary>
		/// The BlackBerry Push Service has an error that prevented the request from being completed.
		/// </summary>
		public static readonly StatusCode RimInternalServerError = new StatusCode("23000", "The BlackBerry Push Service has an error that prevented the request from being completed.");

		static StatusCode()
		{
			_globalCodes = new Dictionary<string, StatusCode>();
			_globalCodes.Add(OK.Code, OK);
			_globalCodes.Add(Accepted.Code, Accepted);
			_globalCodes.Add(OverSoftPushQuota.Code, OverSoftPushQuota);
			_globalCodes.Add(BadRequest.Code, BadRequest);
			_globalCodes.Add(Forbidden.Code, Forbidden);
			_globalCodes.Add(AddressError.Code, AddressError);
			_globalCodes.Add(AddressNotFound.Code, AddressNotFound);
			_globalCodes.Add(PushIdNotFound.Code, PushIdNotFound);
			_globalCodes.Add(InvalidInitiatorFunction.Code, InvalidInitiatorFunction);
			_globalCodes.Add(NotSupportedInitiatorFunction.Code, NotSupportedInitiatorFunction);
			_globalCodes.Add(DuplicatePushId.Code, DuplicatePushId);
			_globalCodes.Add(CancellationNotPossible.Code, CancellationNotPossible);
			_globalCodes.Add(StatusQueryNotPossible.Code, StatusQueryNotPossible);
			_globalCodes.Add(OverHardPushQuota.Code, OverHardPushQuota);
			_globalCodes.Add(InternalServerError.Code, InternalServerError);
			_globalCodes.Add(NotImplemented.Code, NotImplemented);
			_globalCodes.Add(VersionNotSupported.Code, VersionNotSupported);
			_globalCodes.Add(SpecifiedDeliveryMethodNotSupported.Code, SpecifiedDeliveryMethodNotSupported);
			_globalCodes.Add(CapabilitiesNotSupported.Code, CapabilitiesNotSupported);
			_globalCodes.Add(OperationNotSupportedForMultipleRecipients.Code, OperationNotSupportedForMultipleRecipients);
			_globalCodes.Add(UnableToTransformMessage.Code, UnableToTransformMessage);
			_globalCodes.Add(UnableToDeliverMessageUsingDeliveryMethod.Code, UnableToDeliverMessageUsingDeliveryMethod);
			_globalCodes.Add(NetworkUnavailable.Code, NetworkUnavailable);
			_globalCodes.Add(BearerUnavailable.Code, BearerUnavailable);
			_globalCodes.Add(ReplaceNotSupported.Code, ReplaceNotSupported);
			_globalCodes.Add(ServiceFailure.Code, ServiceFailure);
			_globalCodes.Add(ServiceUnavailable.Code, ServiceUnavailable);
			_globalCodes.Add(Expired.Code, Expired);
			_globalCodes.Add(Failed.Code, Failed);
			_globalCodes.Add(PortClosed.Code, PortClosed);
			_globalCodes.Add(ServiceBlocked.Code, ServiceBlocked);
			_globalCodes.Add(PushAbortedByDevice.Code, PushAbortedByDevice);
			_globalCodes.Add(RimRequestOk.Code, RimRequestOk);
			_globalCodes.Add(RimBadRequest.Code, RimBadRequest);
			_globalCodes.Add(RimPushIdNotFound.Code, RimPushIdNotFound);
			_globalCodes.Add(RimAddressError.Code, RimAddressError);
			_globalCodes.Add(RimIncorrectStatus.Code, RimIncorrectStatus);
			_globalCodes.Add(RimNoResults.Code, RimNoResults);
			_globalCodes.Add(RimExceedCallsInPeriod.Code, RimExceedCallsInPeriod);
			_globalCodes.Add(RimInternalServerError.Code, RimInternalServerError);
		}

		private StatusCode(string code, string description)
		{
			Code = code;
			Description = description;
		}

		/// <summary>
		/// Gets a value indicating whether this instance is a PAP status code.
		/// </summary>
		/// <value>
		/// <c>true</c> if this instance is PAP status code; otherwise, <c>false</c>.
		/// </value>
		public bool IsPapCode
		{
			get
			{
				return Code.Length < 5;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is RIM specific status code.
		/// </summary>
		/// <value>
		/// <c>true</c> if this instance is rim code; otherwise, <c>false</c>.
		/// </value>
		public bool IsRimCode
		{
			get
			{
				return !IsPapCode;
			}
		}

		/// <summary>
		/// Gets the code.
		/// </summary>
		/// <value>The code.</value>
		public string Code
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the description.
		/// </summary>
		/// <value>The description.</value>
		public string Description
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the status code corresponding to the specified code value.
		/// </summary>
		/// <param name="code">The code.</param>
		/// <returns></returns>
		public static StatusCode GetCode(int code)
		{
			return GetCode(code.ToString());
		}

		/// <summary>
		/// Gets the status code corresponding to the specified code value.
		/// </summary>
		/// <param name="code">The code.</param>
		/// <returns></returns>
		public static StatusCode GetCode(string code)
		{
			StatusCode sc;
			if (!_globalCodes.TryGetValue(code, out sc))
			{
				sc = new StatusCode(code, string.Empty);
			}
			return sc;
		}

		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals(object obj)
		{
			StatusCode rhs = (StatusCode)obj;
			if (rhs == null)
			{
				return false;
			}
			return Code.Equals(rhs.Code, StringComparison.OrdinalIgnoreCase);
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			return Code.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return string.Format("StatusCode[{0}:{1}]", Code, Description);
		}
	}
}
